home *** CD-ROM | disk | FTP | other *** search
- /*----------------------------------------------------------------------------
- Subprocess.m
-
- From Subprocess example by Charles L. Oei
- pty support by Joe Freeman
- with encouragement from Kristofer Younger
- Subprocess Example, Release 2.0
- NeXT Computer, Inc.
-
- You may freely copy, distribute and reuse the code in this example.
- NeXT disclaims any warranty of any kind, expressed or implied, as to
- its fitness for any particular use.
-
- SYNOPSIS
- Handles a UNIX process that runs asynchronously.
-
- REVISIONS
- Subprocess.m,v
- # Revision 1.4 1993/02/01 02:21:31 nwc
- # Added baud rate button. Cleaned interface. Fixed subprocess bug.
- #
- # Revision 1.3 1992/09/15 14:59:23 nwc
- # Made NeXTSTEP 3.0 compatible.
- # Added baud rate preference since that now works in uuq.
- #
- # Revision 1.2 1992/09/03 16:33:48 nwc
- # Clean up after Subprocesses.
- #
- # Revision 1.1.1.1 1992/08/18 14:34:20 nwc
- # GENESIS
- #
- # Revision 1.1 1992/07/04 03:17:22 nwc
- # Initial revision
- #
- ----------------------------------------------------------------------------*/
- #import <sys/wait.h>
- #import <sys/resource.h>
- #import <appkit/nextstd.h>
- #import <appkit/Application.h>
- #import <appkit/Panel.h>
- #import "Subprocess.h"
-
- extern int wait4(int, union wait *, int, struct rusage *);
- static void fdHandler(int theFd, id self);
- static void stderrFdHandler(int theFd, id self);
-
- #define PIPE_ERROR "Error starting UNIX pipes to subprocess."
- #define VFORK_ERROR "Error starting UNIX vfork of subprocess."
-
- @interface Subprocess(Private)
- - childDidExit;
- - fdHandler:(int)theFd;
- @end
-
- @implementation Subprocess(Private)
-
- /*
- * cleanup after a child process exits
- */
- - childDidExit
- {
- union wait w;
- int status = 0;
- int thePid;
-
- thePid = wait4(childPid, &w, WUNTRACED, NULL);
- #ifdef DEBUG
- fprintf(stderr, "%s: wait4() on process id #%d returned %d, with "
- "w_status = %d, w_retcode = %u, w_stopval = %u, "
- "w_stopsig = %u, w_termsig = %d\n",
- [self name], childPid, thePid, w.w_status, w.w_retcode,
- w.w_stopval, w.w_stopsig, w.w_termsig);
- #endif
- if (thePid > 0)
- {
- if (WIFEXITED(w))
- status = (w.w_status >> 8);
- else
- {
- if (WIFSTOPPED(w))
- status = SUBPROCESS_STOPPED;
- else
- {
- if (WIFSIGNALED(w))
- status = SUBPROCESS_SIGNALED;
- }
- }
- DPSRemoveFD(fromChild);
- DPSRemoveFD(stderrFromChild);
- fclose(fpFromChild);
- close(fromChild);
- close(stderrFromChild);
- fclose(fpToChild);
- running = NO;
- [delegate perform:@selector(subprocess:done:)
- with :self
- with:(void *)status];
- if(markedForFree)
- [NXApp delayedFree: self];
- }
- return (self);
- }
-
- /*
- * DPS handler for output from subprocess
- */
- - fdHandler:(int)theFd
- {
- char *s, *linep;
- int bufferCount;
-
- bufferCount = read(theFd, outputBuffer + outputBufferLen,
- BUFSIZ - outputBufferLen);
- if (bufferCount <= 0)
- {
- [self childDidExit];
- return (self);
- }
- outputBuffer[bufferCount + outputBufferLen] = '\0';
-
- /*
- * Send lines in the buffer to the delegate
- */
- s = linep = outputBuffer;
- while (s != NULL)
- {
- if ((s = index(linep, '\n')) != NULL)
- {
- *s = (char)0;
- [delegate perform:@selector(subprocess:output:)
- with :self
- with:(void *)linep];
- linep = s + 1;
- }
- }
-
- /*
- * Copy the last part of the line back into the input buffer for next time (incomplete line)
- */
- outputBufferLen = strlen(linep);
- strncpy(outputBuffer, linep, outputBufferLen);
-
- return (self);
- }
-
- @end
-
- @implementation Subprocess
-
- /*
- * Cover for the init:withDelegate: with a nil delegate
- */
- - init:(const char *)subprocessString
- {
- return ([self init:subprocessString
- withDelegate:nil]);
- }
-
- - init:(const char *)subprocessString withDelegate:theDelegate
- {
- int pipeTo[2];
- int pipeFrom[2];
- int pipeStderr[2]; /* for stderr to different fd */
- int numFds, fd;
- int processGroup;
-
- [super init];
- markedForFree = NO;
- outputBufferLen = 0;
- stderrBufferLen = 0;
- [self setDelegate:theDelegate];
-
- if (pipe(pipeTo) < 0 || pipe(pipeFrom) < 0 || pipe(pipeStderr) < 0)
- {
- [delegate perform:@selector(subprocess:error:)
- with :self
- with:(void *)PIPE_ERROR];
- return (self);
- }
-
- switch (childPid = vfork())
- {
- case -1: /* error */
- [delegate perform:@selector(subprocess:error:)
- with :self
- with:(void *)VFORK_ERROR];
- return (self);
-
- case 0: /* child */
- dup2(pipeTo[0], 0);
- dup2(pipeFrom[1], 1); /* get stdout from process */
- dup2(pipeStderr[1], 2); /* get stderr here */
- numFds = getdtablesize();
- for (fd = 3; fd < numFds; fd++)
- close(fd);
-
- processGroup = getpid();
- ioctl(0, TIOCSPGRP, (char *)&processGroup);
- setpgrp(0,processGroup);
-
- /*
- * we exec a /bin/sh so that cmds are easier to specify for the user
- */
- execl("/bin/sh", "sh", "-c", subprocessString, 0);
- perror("vfork (child)"); /* should never gets here tho */
- exit(1);
-
- default: /* parent */
- running = YES;
-
- close(pipeTo[0]);
- close(pipeFrom[1]);
- close(pipeStderr[1]);
-
- fpToChild = fdopen(pipeTo[1], "w");
- fromChild = pipeFrom[0];
- fpFromChild = fdopen(pipeFrom[0], "r");
-
- stderrFromChild = pipeStderr[0];
-
- /*
- * Set buffering method, also make it use its own buffers
- */
- setbuf(fpToChild, NULL); /* no buffering */
- setbuf(fpFromChild, NULL);
- DPSAddFD(fromChild, (DPSFDProc) fdHandler, (id) self,
- NX_MODALRESPTHRESHOLD + 1);
-
- DPSAddFD(stderrFromChild, (DPSFDProc) stderrFdHandler, (id) self,
- NX_MODALRESPTHRESHOLD + 1);
-
- return (self);
- }
- }
-
-
- - stderrFdHandler:(int)theFd
- {
- char *s, *linep;
- int bufferCount;
-
- bufferCount = read(theFd, stderrBuffer + stderrBufferLen,
- BUFSIZ - stderrBufferLen);
- if (bufferCount <= 0)
- return (self);
-
- stderrBuffer[bufferCount + stderrBufferLen] = '\0';
-
- /*
- * Send lines in the buffer to the delegate
- */
- s = linep = stderrBuffer;
- while (s != NULL)
- {
- if ((s = index(linep, '\n')) != NULL)
- {
- *s = (char)0;
- [delegate perform:@selector(subprocess:stderrOutput:)
- with :self
- with:(void *)linep];
- linep = s + 1;
- }
- }
-
- /*
- * Copy the last part of the line back into the input buffer for next time (incomplete line)
- */
- stderrBufferLen = strlen(linep);
- strncpy(stderrBuffer, linep, stderrBufferLen);
-
- return (self);
- }
-
- - send:(const char *)string withNewline:(BOOL) wantNewline
- {
- fputs(string, fpToChild);
- if (wantNewline)
- fputc('\n', fpToChild);
- return (self);
- }
-
- - send:(const char *)string
- {
- [self send:string withNewline:YES];
- return (self);
- }
-
- /*
- * Returns the process id of the process (and therefore the process group
- * of the job)
- */
- - (int)pid
- {
- return (childPid);
- }
-
- - (BOOL) isPaused
- {
- return (paused);
- }
-
- - resume:sender
- {
- if (paused)
- {
- killpg(childPid, SIGCONT); /* resume the process group */
- paused = NO;
- }
- return (self);
- }
-
- - pause:sender
- {
- if (!paused)
- {
- killpg(childPid, SIGSTOP); /* pause the process group */
- paused = YES;
- }
- return (self);
- }
-
- - (BOOL) isRunning
- {
- return (running);
- }
-
- - terminate:sender
- {
- if (running)
- killpg(childPid, SIGKILL);
- return (self);
- }
-
- - free
- {
- if(running && !markedForFree)
- {
- markedForFree = YES;
- return [self terminate: self];
- }
- else
- return [super free];
- }
-
- /*
- * effectively sends an EOF to the child process stdin
- */
- - terminateInput
- {
- fclose(fpToChild);
- return (self);
- }
-
- - setDelegate:anObject
- {
- delegate = anObject;
- return (self);
- }
-
- - delegate
- {
- return (delegate);
- }
-
- @end
-
- @implementation Object(SubprocessDelegate)
-
- - subprocess: sender done:(int)exitStatus
- {
- return (self);
- }
-
- - subprocess:sender output:(char *)buffer
- {
- return (self);
- }
-
- - subprocess:sender stderrOutput:(char *)buffer
- {
- return (self);
- }
-
- - subprocess:sender error:(const char *)errorString
- {
- if (NXApp)
- NXRunAlertPanel(0, errorString, 0, 0, 0);
- else
- perror(errorString);
- return (self);
- }
-
- @end
-
-
- typedef struct
- {
- @defs(Object)
- } object;
-
- /*
- * And standard error from subprocess
- */
- static void stderrFdHandler(int theFd, id self)
- {
- if(self && ((object *)self)->isa)
- [self stderrFdHandler:theFd];
- }
-
- /*
- * DPS handler for output from subprocess
- */
- static void fdHandler(int theFd, id self)
- {
- if(self && ((object *)self)->isa)
- [self fdHandler:theFd];
- }
-
-
-